// I2C library
//////////////////////
// Copyright(C) 2012
// Francesco Ferrara
// ferrara[at]libero[point]it
//////////////////////

/*
FastWire 
- 0.24
 added stop
- 0.23
 added reset
 
 This is a library to help faster programs to read I2C devices.
 Copyright(C) 2012 Francesco Ferrara
 occhiobello at gmail dot com
 */

#ifndef H_FASTWIRE
#define H_FASTWIRE

#include "Arduino.h"

class Fastwire
{
private:
  static const byte 
  /* Master */
  TW_START                      =0x08,
  TW_REP_START                  =0x10,
  /* Master Transmitter */
  TW_MT_SLA_ACK		        =0x18,
  TW_MT_SLA_NACK		=0x20,
  TW_MT_DATA_ACK		=0x28,
  TW_MT_DATA_NACK		=0x30,
  TW_MT_ARB_LOST		=0x38,
  /* Master Receiver */
  TW_MR_ARB_LOST		=0x38,
  TW_MR_SLA_ACK		        =0x40,
  TW_MR_SLA_NACK		=0x48,
  TW_MR_DATA_ACK		=0x50,
  TW_MR_DATA_NACK		=0x58,
  TW_OK = 0,
  TW_ERROR= 1;
  static boolean waitInt()
  {
    int l=250;

    while (!(TWCR & (1<<TWINT)) && l-->0);
    return l>0;
  }

public:
  void reset()
  {
    TWCR=0;
  }

  static void setup(int khz,boolean pullup)
  {
    TWCR=0;
#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
    if (pullup)
      PORTD |= ((1<<0)|(1<<1)); // TODO others micro
    else
      PORTD &= ~((1<<0)|(1<<1)); // TODO others micro
#else
    if (pullup)
      PORTC |= ((1<<4)|(1<<5)); // TODO others micro
    else
      PORTC &= ~((1<<4)|(1<<5)); // TODO others micro
#endif
    TWSR = 0;        // no prescaler => prescaler = 1
    TWBR = ((16000L / khz) - 16) / 2; // change the I2C clock rate
    TWCR = 1<<TWEN;  // enable twi module, no interrupt
  }

  static byte stop()
  {
    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
    if (!waitInt()) return 1;
    return 0;
  }

  static byte write(byte device, byte address, byte value)
  {
    byte twst,retry;

    retry=2;
    do
    {
      TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO) | (1<<TWSTA)   ; 
      if (!waitInt()) return 1;
      twst=TWSR & 0xF8;
      if (twst != TW_START && twst != TW_REP_START) return 2;

      TWDR = device&0xFE; // send device address without read bit (1)
      TWCR = (1<<TWINT) | (1<<TWEN);
      if (!waitInt()) return 3;
      twst=TWSR & 0xF8;
    } 
    while (twst == TW_MT_SLA_NACK && retry-- > 0);
    if (twst != TW_MT_SLA_ACK) return 4;


    TWDR = address; // send data to the previously addressed device
    TWCR = (1<<TWINT) | (1<<TWEN);
    if (!waitInt()) return 5;
    twst=TWSR & 0xF8;
    if (twst != TW_MT_DATA_ACK) return 6;

    TWDR = value; // send data to the previously addressed device
    TWCR = (1<<TWINT) | (1<<TWEN);
    if (!waitInt()) return 7;
    twst=TWSR & 0xF8;
    if (twst != TW_MT_DATA_ACK) return 8;

    return 0;
  }

  static byte readBuf(byte device,byte address,byte *data,byte num)
  {
    byte twst,retry;

    retry=2;
    do
    {
      TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO)  | (1<<TWSTA)  ; 
      if (!waitInt()) return 16;
      twst=TWSR & 0xF8;
      if (twst != TW_START && twst != TW_REP_START) return 17;


      TWDR = device & 0xfe; // send device address to write
      TWCR = (1<<TWINT) | (1<<TWEN);
      if (!waitInt()) return 18;
      twst=TWSR & 0xF8;
    } 
    while (twst == TW_MT_SLA_NACK && retry-- > 0);
    if (twst != TW_MT_SLA_ACK) return 19;

    TWDR = address; // send data to the previously addressed device
    TWCR = (1<<TWINT) | (1<<TWEN);
    if (!waitInt()) return 20;
    twst=TWSR & 0xF8;
    if (twst != TW_MT_DATA_ACK) return 21;




    /***/

    retry=2;
    do
    {
      TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO) | (1<<TWSTA)  ; 
      if (!waitInt()) return 22;
      twst=TWSR & 0xF8;
      if (twst != TW_START && twst != TW_REP_START) return 23;

      TWDR = device|0x01; // send device address with the read bit (1)
      TWCR = (1<<TWINT) | (1<<TWEN);
      if (!waitInt()) return 24;
      twst=TWSR & 0xF8;
    } 
    while (twst == TW_MR_SLA_NACK && retry-- > 0);
    if (twst != TW_MR_SLA_ACK) return 25;

    for(uint8_t i=0;i<num;i++)
    {
      if (i==num-1)
        TWCR = (1<<TWINT) | (1<<TWEN) ;
      else
        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
      if (!waitInt()) return 26;
      twst=TWSR & 0xF8;
      if (twst != TW_MR_DATA_ACK && twst != TW_MR_DATA_NACK) return twst;
      data[i]=TWDR;
    }

    return 0;

  }

} 
fw;

#endif
































